#ifndef __M__HELPER_H_
#define __M__HELPER_H_
#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <cstring>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <atomic>
#include <sys/stat.h>
#include <sqlite3.h>
#include "Logger.hpp"

using std::cout;
using std::endl;
using std::string;
using std::vector;

namespace mq
{
    class SqliteHelper;
    class StrHelper;
    class UUIDHelper;
    class FileHelper;

    // 封装一个sqliteHelper类，封装简单的sqlite数据库操作接口，完成数据库的基础增删改查功能
    class SqliteHelper
    {
    public:
        typedef int (*SqliteCallback)(void *, int, char **, char **);
        SqliteHelper(const std::string &dbfile) : _dbfile(dbfile) {}
        bool open(int safe_level = SQLITE_OPEN_FULLMUTEX) // 打开数据库文件：默认串行模式
        {
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE /*读写*/ | SQLITE_OPEN_CREATE /*创建*/ | safe_level /*安全模式*/, NULL);
            if (ret != SQLITE_OK)
            {
                ELOG("创建/打开数据库文件: %s 失败 -> %s", _dbfile, sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        bool exec(const string &sql, SqliteCallback cb, void *args) // 执行sql语句：增删改查，并通过回调函数返回结果
        {
            // int sqlite3_exec(sqlite3 *, char *sql, int (*callback)(void *, int, char **, char **), void *arg, char **err);
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, args, NULL);
            if (ret != SQLITE_OK)
            {
                ELOG("执行sql语句: %s 失败 -> %s", sql.c_str(), sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        void close()
        {
            if (_handler)
                sqlite3_close_v2(_handler);
        }

    private:
        sqlite3 *_handler;   // 数据库操作句柄
        std::string _dbfile; // 数据库文件名称
        // 一库一文件
    };

    // 字符串操作类
    class StrHelper
    {
    public:
        static size_t split(const string &str, const string &sep, vector<string> &result)
        {
            // 对str根据sep进行分割，分割加过保存在result中
            int cnt = 0;
            size_t start = 0;
            while (start < str.size())
            {
                ssize_t pos = str.find(sep, start);
                if (pos == string::npos)
                {
                    string tmp = str.substr(start);
                    if (!tmp.empty())
                    {
                        result.push_back(tmp);
                        cnt++;
                    }
                    break;
                }
                if (start == pos) // 说明有两个分隔符连在一起，且中间没有数据
                {
                    start += sep.size();
                    continue;
                }
                result.push_back(str.substr(start, pos - start));
                cnt++;
                start = pos + sep.size();
            }
            return cnt;
        }
    };

    // 生成8-4-4-4-12uuid
    class UUIDHelper
    {
    public:
        static string uuid()
        {
            std::random_device rd;

            std::mt19937_64 generater(rd());

            std::uniform_int_distribution<int> distribution(0, 255);

            std::stringstream ss;
            for (int i = 0; i < 8; i++)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generater);
                if (i == 3 || i == 5 || i == 7) // 8-4-4
                    ss << "-";
            }
            static std::atomic<size_t> seq(1); // 自增序号
            size_t num = seq.fetch_add(1);
            for (int i = 7; i >= 0; i--)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (8 * i)) & 0xff);
                if (i == 6) // 4-12
                    ss << "-";
            }
            return ss.str();
        }
    };

    // 文件操作类
    class FileHelper
    {
    public:
        FileHelper(const string &filename) : _filename(filename) {}
        static bool exists(const string &filename) // 判断文件是否存在
        {
            struct stat st;
            return (stat(filename.c_str(), &st) == 0); // stat查看文件权限，如果文件存在，返回0表示查看成功
        }
        size_t size() // 获取文件大小
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if (ret < 0)
            {
                ELOG("%s 获取文件大小失败，文件可能不存在", _filename.c_str());
                return 0; // 文件不存在，返回0表示不可获取大小
            }
            return st.st_size;
        }
        static size_t size(const string &filename) // 获取文件大小
        {
            FileHelper fh(filename);
            return fh.size();
        }
        bool read(char *body)
        {
            size_t fszie = this->size();
            return read(body, 0, fszie);
        }
        bool read(char *body, size_t pos, size_t len) // 从文件pos位置读取len字节到body中
        {
            // 1.打开文件流
            std::fstream ifs(_filename.c_str(), std::ios::binary | std::ios::in); // 二进制读取打开
            if (!ifs.is_open())
            {
                ELOG("%s 打开文件失败", _filename.c_str());
                return false;
            }
            // 2.跳转到读取位置
            ifs.seekg(pos, std::ios::beg); // 相对于begin位置偏移pos字节
            // 3.读取数据
            ifs.read(body, len);
            if (!ifs.good())
            {
                ELOG("%s 读取文件失败", _filename.c_str());
                ifs.close();
                return false;
            }
            // 4.关闭文件流
            ifs.close();
            return true;
        }
        bool write(const char *body)
        {
            if (!exists(_filename))
                createFile();
            else
                clearFile();
            size_t len = std::strlen(body);
            // cout << "len:" << len << endl;
            return write(body, 0, len);
        }
        bool write(const char *body, size_t pos, size_t len) // 将len长度数据body写入文件pos位置
        {
            if (!exists(_filename))
                createFile(); // 没有文件就创建文件
            // 1.打开文件
            std::fstream iofs(_filename.c_str(), std::ios::binary | std::ios::in | std::ios::out); // 想要任意位置写，就需要跳转文件位置，跳转需要读权限
            if (!iofs.is_open())
            {
                ELOG("%s 打开文件失败", _filename.c_str());
                return false;
            }
            // 2.跳转文件位置
            iofs.seekg(pos, std::ios::beg);
            // 3.写入数据
            iofs.write(body, len);
            if (!iofs.good())
            {
                ELOG("%s 写入文件失败", _filename.c_str());
                iofs.close();
                return false;
            }
            iofs.close();
            return false;
        }
        bool rename(const string &nname) // 重命名文件
        {
            bool ret = (std::rename(_filename.c_str(), nname.c_str()) == 0);
            _filename = nname;
            return ret;
        }
        bool createFile() // 创建文件：写方式打开再关闭
        {
            string parentPath = parentDirectory(_filename); // 所在目录
            createDirectory(parentPath);                    // 先创建所在目录，再创建文件

            std::fstream fs(_filename.c_str(), std::ios::binary | std::ios::out);
            if (!fs.is_open())
            {
                ELOG("%s 创建文件失败", _filename.c_str());
                return false;
            }
            fs.close();
            return true;
        }
        static bool createFile(const string &filename)
        {
            if (exists(filename))
                return true;
            string parentPath = parentDirectory(filename); // 所在目录
            createDirectory(parentPath);                   // 先创建所在目录，再创建文件

            std::fstream fs(filename.c_str(), std::ios::binary | std::ios::out);
            if (!fs.is_open())
            {
                ELOG("%s 创建文件失败", filename.c_str());
                return false;
            }
            fs.close();
            return true;
        }
        bool removeFile() // 移除文件
        {
            return (std::remove(_filename.c_str()) == 0);
        }
        static bool createDirectory(const string &path) // 创建文件目录：a/b/c/
        {
            // cout << "path:" << path << endl;
            vector<string> path_list;
            size_t n = StrHelper::split(path, "/", path_list);
            string dir = "./";
            for (const auto &str : path_list) // 循环创建多级目录
            {
                if (str == ".")
                    continue; // 当前目录，不需要创建
                else if (str == "..")
                {
                    if (dir == "./")
                        dir = "../";
                    else
                        dir += "../";
                    continue;
                }
                dir += str;
                if (exists(dir))
                {
                    dir += "/";
                    continue; // 目录已存在，跳过
                }
                int ret = mkdir(dir.c_str(), 0775); // other不具有写权限：不能在该目录下创建和删除文件
                dir += "/";
                if (ret < 0)
                {
                    ELOG("%s 创建目录失败", dir.c_str());
                    return false;
                }
                // cout << "dir:" << dir << endl;
            }
            return true;
        }
        static bool removeDirectory(const string &path) // 移除目录及目录下所有文件
        {
            // system()：执行cmd命令
            string cmd = "rm -rf " + path;
            return std::system(cmd.c_str()) != -1;
        }
        static string parentDirectory(const string &filename) // 获取该文件所在目录
        {
            size_t pos = filename.find_last_of("/");
            if (pos == string::npos) // 找不到路径分隔符，说明这是一个当前路径的文件名
                return ".";
            return filename.substr(0, pos);
        }
        string get_filename() // 获取文件名
        {
            return _filename;
        }
        void clearFile() // 清空文件
        {
            if (exists(_filename))
                removeFile();
            createFile();
        }

    private:
        string _filename;
    };

}

#endif
